home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 1 / QRZ Ham Radio Callsign Database - December 1993.iso / ucsd / packet / tcpip / amiga / asrc29k.lha / ftpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-08  |  21.8 KB  |  971 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "session.h"
  6. #include "cmdparse.h"
  7. #include "timer.h"
  8. #include "proc.h"
  9. #include "tty.h"
  10. #include "socket.h"
  11. #include "ftp.h"
  12. #include "ftpcli.h"
  13. #include "commands.h"
  14. #include "netuser.h"
  15. #include "config.h"
  16.  
  17. #define    DIRBUF    256
  18.  
  19. static int doascii __ARGS((int argc,char *argv[],void *p));
  20. static int dobinary __ARGS((int argc,char *argv[],void *p));
  21. static int doftpcd __ARGS((int argc,char *argv[],void *p));
  22. static int doget __ARGS((int argc,char *argv[],void *p));
  23. static int dohash __ARGS((int argc,char *argv[],void *p));
  24. static int doverbose __ARGS((int argc,char *argv[],void *p));
  25. static int dolist __ARGS((int argc,char *argv[],void *p));
  26. static int dols __ARGS((int argc,char *argv[],void *p));
  27. static int domkdir __ARGS((int argc,char *argv[],void *p));
  28. static int domget __ARGS((int argc,char *argv[],void *p));
  29. static int donothing __ARGS((int argc,char *argv[],void *p));
  30. static int doput __ARGS((int argc,char *argv[],void *p));
  31. static int doquit __ARGS((int argc,char *argv[],void *p));
  32. static int dormdir __ARGS((int argc,char *argv[],void *p));
  33. static int dotype __ARGS((int argc,char *argv[],void *p));
  34. static int getline __ARGS((struct session *sp,char *prompt,char *buf,int n));
  35. static int getresp __ARGS((struct ftpcli *ftp,int mincode));
  36. static int getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
  37.     char *localname));
  38. static void sendport __ARGS((int s,struct sockaddr_in *socket));
  39.  
  40. static char *ftpcli_login __ARGS((struct ftpcli *ftp,char *host));
  41.  
  42. static char Notsess[] = "Not an FTP session!\n";
  43.  
  44. static struct cmds Ftpcmds[] = {
  45.     "",        donothing,    0, 0, NULLCHAR,
  46.     "ascii",    doascii,    0, 0, NULLCHAR,
  47.     "binary",    dobinary,    0, 0, NULLCHAR,
  48.     "cd",        doftpcd,    0, 2, "cd <directory>",
  49.     "dir",        dolist,        0, 0, NULLCHAR,
  50.     "hash",        dohash,        0, 0, NULLCHAR,
  51.     "list",        dolist,        0, 0, NULLCHAR,
  52.     "get",        doget,        0, 2, "get remotefile <localfile>",
  53.     "ls",        dols,        0, 0, NULLCHAR,
  54.     "mget",        domget,        0, 2, "mget <file> [<file> ...]",
  55.     "mkdir",    domkdir,    0, 2, "mkdir <directory>",
  56.     "nlst",        dols,        0, 0, NULLCHAR,
  57.     "quit",        doquit,        0, 0, NULLCHAR,
  58.     "rmdir",    dormdir,    0, 2, "rmdir <directory>",
  59.     "put",        doput,        0, 2, "put localfile <remotefile>",
  60.     "type",        dotype,        0, 0, NULLCHAR,
  61.     "verbose",    doverbose,    0, 0, NULLCHAR,
  62.     NULLCHAR,    NULLFP,        0, 0, NULLCHAR,
  63. };
  64.  
  65. /* Handle top-level FTP command */
  66. int doftp(argc,argv,p)
  67. int argc;
  68. char *argv[];
  69. void *p;
  70. {
  71.     struct session *sp;
  72.     struct ftpcli ftp;
  73.     struct sockaddr_in fsocket;
  74.     int resp,vsave;
  75.     char *buf,*bufsav,*cp;
  76.     int control;
  77.  
  78.     /* Allocate a session control block */
  79.     if((sp = newsession(argv[1],FTP)) == NULLSESSION){
  80.         tprintf("Too many sessions\n");
  81.         freeargs(argc,argv);
  82.         return 1;
  83.     }
  84.     memset((char *)&ftp,0,sizeof(ftp));
  85.     ftp.control = ftp.data = -1;
  86.     ftp.verbose = V_BYTE;
  87.     ftp.type = IMAGE_TYPE;
  88.  
  89.     sp->cb.ftp = &ftp;    /* Downward link */
  90.     ftp.session = sp;    /* Upward link */
  91.  
  92.     fsocket.sin_family = AF_INET;
  93.     if(argc < 3)
  94.         fsocket.sin_port = IPPORT_FTP;
  95.     else 
  96.         fsocket.sin_port = atoi(argv[2]);
  97.  
  98.     freeargs(argc,argv);
  99.     tprintf("Resolving %s... ",sp->name);
  100.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  101.         tprintf(Badhost,sp->name);
  102.         keywait(NULLCHAR,1);
  103.         freesession(sp);
  104.         return 1;
  105.     }
  106.     /* Open the control connection */
  107.     if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
  108.         tprintf("Can't create socket\n");
  109.         keywait(NULLCHAR,1);
  110.         freesession(sp);
  111.         return 1;
  112.     }
  113.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  114.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  115.         goto quit;
  116.     tprintf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
  117.         sp->name);
  118.  
  119.     /* Wait for greeting from server */
  120.     resp = getresp(&ftp,200);
  121.  
  122.     if(resp >= 400)
  123.         goto quit;
  124.     /* Now process responses and commands */
  125.     buf = mallocw(LINELEN);
  126.     while(resp != -1){
  127.         if(resp == 220) {
  128.             /* Sign-on banner; prompt for and send USER command */
  129.             if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR){
  130.                 getline(sp,"Enter user name: ",buf,LINELEN);
  131.                 /* Send the command only if the user response
  132.                  * was non-null
  133.                  */
  134.                 if(buf[0] != '\n') {
  135.                     usprintf(control,"USER %s",buf);
  136.                     resp = getresp(&ftp,200);
  137.                 } else
  138.                     resp = 200;    /* dummy */
  139.             } else {
  140.                 usprintf(control,"USER %s\n",cp);
  141.                 free(cp);
  142.                 resp = getresp(&ftp,200);
  143.             }
  144.         } else if(resp == 331) {
  145.             if(ftp.password == NULLCHAR){
  146.                 /* turn off echo */
  147.                 sp->ttystate.echo = 0;
  148.                 getline(sp,"Password: ",buf,LINELEN);
  149.                 tprintf("\n");
  150.                 /* Turn echo back on */
  151.                 sp->ttystate.echo = 1;
  152.                 /* Send the command only if the user response
  153.                  * was non-null
  154.                  */
  155.                 if(buf[0] != '\n') {
  156.                     usprintf(control,"PASS %s",buf);
  157.                     resp = getresp(&ftp,200);
  158.                 } else
  159.                     resp = 200;    /* dummy */
  160.             } else {
  161.                 usprintf(control,"PASS %s\n",ftp.password);
  162.                 resp = getresp(&ftp,200);
  163.                 free(ftp.password);
  164.                 ftp.password = NULLCHAR;    /* clean up */
  165.             }
  166.         } else {
  167.             /* Test the control channel first */
  168.             if(sockstate(control) == NULLCHAR)
  169.                 break;
  170.  
  171.             getline(sp,"ftp> ",buf,LINELEN);
  172.  
  173.             /* Copy because cmdparse modifies the original */
  174.             bufsav = strdup(buf);
  175.             if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1){
  176.                 /* Valid command, free buffer and get another */
  177.                 free(bufsav);
  178.             } else {
  179.                 /* Not a local cmd, send to remote server */
  180.                 usputs(control,bufsav);
  181.                 free(bufsav);
  182.  
  183.                 /* Enable display of server response */
  184.                 vsave = ftp.verbose;
  185.                 ftp.verbose = V_NORMAL;
  186.                 resp = getresp(&ftp,200);
  187.                 ftp.verbose = vsave;
  188.             }
  189.         }
  190.     }
  191.     free(buf);
  192. quit:    cp = sockerr(control);
  193.     tprintf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
  194.      cp != NULLCHAR ? cp : "EOF");
  195.  
  196.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  197.         fclose(ftp.fp);
  198.     if(ftp.data != -1)
  199.         close_s(ftp.data);
  200.     if(ftp.control != -1)
  201.         close_s(ftp.control);
  202.     keywait(NULLCHAR,1);
  203.     if(ftp.session != NULLSESSION)
  204.         freesession(ftp.session);
  205.     return 0;
  206. }
  207.  
  208. /* Control verbosity level */
  209. static int
  210. doverbose(argc,argv,p)
  211. int argc;
  212. char *argv[];
  213. void *p;
  214. {
  215.     register struct ftpcli *ftp;
  216.  
  217.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  218.         return -1;
  219.     return setshort(&ftp->verbose,"Verbose",argc,argv);
  220. }
  221.  
  222. /* Set verbosity to high (convenience command) */
  223. static int
  224. dohash(argc,argv,p)
  225. int argc;
  226. char *argv[];
  227. void *p;
  228. {
  229.     register struct ftpcli *ftp;
  230.  
  231.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  232.         return -1;
  233.     ftp->verbose = V_HASH;
  234.     tprintf("Hash Marks every 1024 bytes\n");
  235.     return 0;
  236. }
  237.     
  238. /* Handle null line to avoid trapping on first command in table */
  239. static int
  240. donothing(argc,argv,p)
  241. int argc;
  242. char *argv[];
  243. void *p;
  244. {
  245.     return 0;
  246. }
  247.  
  248. /* Close session */
  249. static int
  250. doquit(argc,argv,p)
  251. int argc;
  252. char *argv[];
  253. void *p;
  254. {
  255.     register struct ftpcli *ftp;
  256.  
  257.     ftp = (struct ftpcli *)p;
  258.     if(ftp == NULLFTP)
  259.         return -1;
  260.     usprintf(ftp->control,"QUIT\n");
  261.     getresp(ftp,200);    /* Get the closing message */
  262.     getresp(ftp,200);    /* Wait for the server to close */
  263.     return -1;
  264. }
  265.  
  266. /* Translate 'cd' to 'cwd' for convenience */
  267. static int
  268. doftpcd(argc,argv,p)
  269. int argc;
  270. char *argv[];
  271. void *p;
  272. {
  273.     register struct ftpcli *ftp;
  274.  
  275.     ftp = (struct ftpcli *)p;
  276.     if(ftp == NULLFTP)
  277.         return -1;
  278.     usprintf(ftp->control,"CWD %s\n",argv[1]);
  279.     return getresp(ftp,200);
  280. }
  281.  
  282. /* Get a collection of files */
  283. static int
  284. domget(argc,argv,p)
  285. int argc;
  286. char *argv[];
  287. void *p;
  288. {
  289.     register struct ftpcli *ftp;
  290.     FILE *files, *filel;
  291.     char tmpname[80];
  292.     char *buf, *local;
  293.     int i;
  294.     long r;
  295.  
  296.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  297.         tprintf(Notsess);
  298.         return 1;
  299.     }
  300.     tmpnam(tmpname);
  301.     buf = mallocw(DIRBUF);
  302.     ftp->state = RECEIVING_STATE;
  303.     for(i=1;i<argc;i++){
  304.         if(argv[i][0] == '@') {
  305.             if((filel = fopen(&argv[i][1], "r")) == NULLFILE){
  306.                 tprintf("Can't open listfile: %s\n", &argv[i][1]);
  307.                 continue;
  308.             }
  309.             if((files = fopen(tmpname, "w")) == NULLFILE){
  310.                 tprintf("Can't open tempfile: %s\n", tmpname);
  311.                 fclose(filel);
  312.                 continue;
  313.             }
  314.             while(fgets(buf,DIRBUF,filel) != NULLCHAR){
  315.                 fputs(buf,files);
  316.             }
  317.             fclose(files);
  318.             fclose(filel);
  319.             if((files = fopen(tmpname, "r")) == NULLFILE){
  320.                 tprintf("Can't open tempfile: %s\n", tmpname);
  321.                 fclose(filel);
  322.                 continue;
  323.             }
  324.         } else {
  325.             r = getsub(ftp,"NLST",argv[i],tmpname);
  326.             if(ftp->abort)
  327.                 break;    /* Aborted */
  328.             if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
  329.                 tprintf("Can't NLST %s\n",argv[i]);
  330.                 unlink(tmpname);
  331.                 continue;
  332.             }
  333.         }
  334.         /* The tmp file now contains a list of the remote files, so
  335.          * go get 'em. Break out if the user signals an abort.
  336.          */
  337.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  338.             rip(buf);
  339.             if(buf[strlen(buf)-1] == '\r')
  340.                 buf[strlen(buf)-1] = '\0';
  341.             local = strdup(buf);
  342.             getsub(ftp,"RETR",buf,local); 
  343.             free(local);
  344.             if(ftp->abort){
  345.                 /* User abort */
  346.                 ftp->abort = 0;
  347.                 fclose(files);
  348.                 unlink(tmpname);
  349.                 free(buf);
  350.                 ftp->state = COMMAND_STATE;
  351.                 return 1;
  352.             }
  353.         }
  354.         fclose(files);
  355.         unlink(tmpname);
  356.     }
  357.     free(buf);
  358.     ftp->state = COMMAND_STATE;
  359.     ftp->abort = 0;
  360.     return 0;
  361. }
  362.  
  363. /* Translate 'mkdir' to 'xmkd' for convenience */
  364. static int
  365. domkdir(argc,argv,p)
  366. int argc;
  367. char *argv[];
  368. void *p;
  369. {
  370.     register struct ftpcli *ftp;
  371.  
  372.     ftp = (struct ftpcli *)p;
  373.     if(ftp == NULLFTP)
  374.         return -1;
  375.     usprintf(ftp->control,"XMKD %s\n",argv[1]);
  376.     return getresp(ftp,200);
  377. }
  378.  
  379. /* Translate 'rmdir' to 'xrmd' for convenience */
  380. static int
  381. dormdir(argc,argv,p)
  382. int argc;
  383. char *argv[];
  384. void *p;
  385. {
  386.     register struct ftpcli *ftp;
  387.  
  388.     ftp = (struct ftpcli *)p;
  389.     if(ftp == NULLFTP)
  390.         return -1;
  391.     usprintf(ftp->control,"XRMD %s\n",argv[1]);
  392.     return getresp(ftp,200);
  393. }
  394.  
  395. static int
  396. dobinary(argc,argv,p)
  397. int argc;
  398. char *argv[];
  399. void *p;
  400. {
  401.     char *args[2];
  402.  
  403.     args[1] = "I";
  404.     return dotype(2,args,p);
  405. }
  406.  
  407. static int
  408. doascii(argc,argv,p)
  409. int argc;
  410. char *argv[];
  411. void *p;
  412. {
  413.     char *args[2];
  414.  
  415.     args[1] = "A";
  416.     return dotype(2,args,p);
  417. }
  418.  
  419. /* Handle "type" command from user */
  420. static int
  421. dotype(argc,argv,p)
  422. int argc;
  423. char *argv[];
  424. void *p;
  425. {
  426.     register struct ftpcli *ftp;
  427.  
  428.     ftp = (struct ftpcli *)p;
  429.     if(ftp == NULLFTP)
  430.         return -1;
  431.     if(argc < 2){
  432.         switch(ftp->type){
  433.         case IMAGE_TYPE:
  434.             tprintf("Image\n");
  435.             break;
  436.         case ASCII_TYPE:
  437.             tprintf("Ascii\n");
  438.             break;
  439.         case LOGICAL_TYPE:
  440.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  441.             break;
  442.         }
  443.         return 0;
  444.     }
  445.     switch(*argv[1]){
  446.     case 'i':
  447.     case 'I':
  448.     case 'b':
  449.     case 'B':
  450.         ftp->typesent = ftp->type = IMAGE_TYPE;
  451.         usprintf(ftp->control,"TYPE I\n");
  452.         break;
  453.     case 'a':
  454.     case 'A':
  455.         ftp->typesent = ftp->type = ASCII_TYPE;
  456.         usprintf(ftp->control,"TYPE A\n");
  457.         break;
  458.     case 'L':
  459.     case 'l':
  460.         ftp->typesent = ftp->type = LOGICAL_TYPE;
  461.         ftp->logbsize = atoi(argv[2]);
  462.         usprintf(ftp->control,"TYPE L %s\n",argv[2]);
  463.         break;
  464.     default:
  465.         tprintf("Invalid type %s\n",argv[1]);
  466.         return 1;
  467.     }
  468.     return getresp(ftp,200);
  469. }
  470.  
  471. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  472. static int
  473. doget(argc,argv,p)
  474. int argc;
  475. char *argv[];
  476. void *p;
  477. {
  478.     char *remotename,*localname;
  479.     register struct ftpcli *ftp;
  480.  
  481.     ftp = (struct ftpcli *)p;
  482.     if(ftp == NULLFTP){
  483.         tprintf(Notsess);
  484.         return 1;
  485.     }
  486.     remotename = argv[1];
  487.     if(argc < 3)
  488.         localname = remotename;
  489.     else
  490.         localname = argv[2];
  491.  
  492.     return getsub(ftp,"RETR",remotename,localname);
  493. }
  494.  
  495. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  496. static int
  497. dolist(argc,argv,p)
  498. int argc;
  499. char *argv[];
  500. void *p;
  501. {
  502.     char *remotename,*localname;
  503.     register struct ftpcli *ftp;
  504.  
  505.     ftp = (struct ftpcli *)p;
  506.     if(ftp == NULLFTP){
  507.         tprintf(Notsess);
  508.         return 1;
  509.     }
  510.     remotename = argv[1];
  511.     if(argc > 2)
  512.         localname = argv[2];
  513.     else
  514.         localname = NULLCHAR;
  515.     return getsub(ftp,"LIST",remotename,localname);
  516. }
  517.  
  518. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  519. static int
  520. dols(argc,argv,p)
  521. int argc;
  522. char *argv[];
  523. void *p;
  524. {
  525.     char *remotename,*localname;
  526.     register struct ftpcli *ftp;
  527.  
  528.     ftp = (struct ftpcli *)p;
  529.     if(ftp == NULLFTP){
  530.         tprintf(Notsess);
  531.         return 1;
  532.     }
  533.     remotename = argv[1];
  534.     if(argc > 2)
  535.         localname = argv[2];
  536.     else
  537.         localname = NULLCHAR;
  538.     return getsub(ftp,"NLST",remotename,localname);
  539. }
  540.  
  541. /* Common code to LIST/NLST/RETR */
  542. static int
  543. getsub(ftp,command,remotename,localname)
  544. register struct ftpcli *ftp;
  545. char *command,*remotename,*localname;
  546. {
  547.     unsigned long total;
  548.     FILE *fp;
  549.     int cnt,resp,i,control;
  550.     char *mode;
  551.     struct sockaddr_in lsocket;
  552.     struct sockaddr_in lcsocket;
  553.     int32 startclk,rate;
  554.     int vsave;
  555.  
  556.     if(ftp == NULLFTP)
  557.         return -1;
  558.     control = ftp->control;
  559.  
  560.     switch(ftp->type){
  561.     case IMAGE_TYPE:
  562.     case LOGICAL_TYPE:
  563.         mode = WRITE_BINARY;
  564.         break;
  565.     case ASCII_TYPE:
  566.         mode = WRITE_TEXT;
  567.         break;
  568.     }
  569.     /* Send TYPE message, if necessary */
  570.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  571.         if(ftp->typesent != ASCII_TYPE){
  572.             /* Directory listings are always in ASCII */
  573.             usprintf(control,"TYPE A\n");
  574.             ftp->typesent = ASCII_TYPE;
  575.             resp = getresp(ftp,200);
  576.             if(resp == -1 || resp > 299){
  577.                 return 1;
  578.             }
  579.         }
  580.     } else if(ftp->typesent != ftp->type){
  581.         switch(ftp->type){
  582.         case ASCII_TYPE:
  583.             usprintf(control,"TYPE A\n");
  584.             break;
  585.         case IMAGE_TYPE:
  586.             usprintf(control,"TYPE I\n");
  587.             break;
  588.         case LOGICAL_TYPE:
  589.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  590.             break;
  591.         }
  592.         ftp->typesent = ftp->type;
  593.         resp = getresp(ftp,200);
  594.         if(resp == -1 || resp > 299){
  595.             return 1;
  596.         }
  597.     }
  598.     if(localname == NULLCHAR){
  599.         fp = NULLFILE;
  600.     } else if((fp = fopen(localname,mode)) == NULLFILE){
  601.         tprintf("Can't write %s: %s\n",localname,sys_errlist[errno]);
  602.         return 1;
  603.     }
  604.     /* Open the data connection */
  605.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  606.     listen(ftp->data,0);    /* Accept only one connection */
  607.     ftp->state = RECEIVING_STATE;
  608.  
  609.     /* Send the PORT message and wait for ack. Use the IP address
  610.      * on the local end of our control connection.
  611.      */
  612.     i = SOCKSIZE;
  613.     getsockname(ftp->data,(char *)&lsocket,&i);
  614.     i = SOCKSIZE;
  615.     getsockname(ftp->control,(char *)&lcsocket,&i);
  616.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  617.     sendport(control,&lsocket);
  618.     resp = getresp(ftp,200);
  619.     if(resp == -1 || resp > 299){
  620.         /* Error, quit */
  621.         if(fp != NULLFILE && fp != stdout)
  622.             fclose(fp);
  623.         close_s(ftp->data);
  624.         ftp->data = -1;
  625.         ftp->state = COMMAND_STATE;
  626.         return 1;
  627.     }
  628.     /* Generate the command to start the transfer and wait for ack */
  629.     if(remotename != NULLCHAR)
  630.         usprintf(control,"%s %s\n",command,remotename);
  631.     else
  632.         usprintf(control,"%s\n",command);
  633.     /* Get the intermediate "150" response */
  634.     resp = getresp(ftp,100);
  635.     if(resp == -1 || resp >= 400){
  636.         /* Error, quit */
  637.         if(fp != NULLFILE && fp != stdout)
  638.             fclose(fp);
  639.         close_s(ftp->data);
  640.         ftp->data = -1;
  641.         ftp->state = COMMAND_STATE;
  642.         return 1;
  643.     }
  644.     /* Wait for the server to open the data connection */
  645.     cnt = 0;
  646.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  647.     startclk = Clock;
  648.  
  649.     /* If output is to the screen, temporarily disable hash marking */
  650.     vsave = ftp->verbose;
  651.     if(vsave >= V_HASH && fp == NULLFILE)
  652.         ftp->verbose = V_NORMAL;
  653.     total = recvfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  654.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  655.      * wait for the data connection to close completely before returning
  656.      * the completion message on the control channel
  657.      */
  658.     close_s(ftp->data);
  659.     ftp->data = -1;
  660.  
  661. #ifdef    CPM
  662.     if(fp != NULLFILE && ftp->type == ASCII_TYPE)
  663.         fputc(CTLZ,fp);
  664. #endif
  665.     if(fp != NULLFILE && fp != stdout)
  666.         fclose(fp);
  667.     startclk = Clock - startclk;
  668.     if(startclk != 0)
  669.         rate = total/startclk;
  670.     else
  671.         rate = 0;
  672.     if(total != -1) {
  673.         if(ftp->verbose >= V_HASH)
  674.             tprintf("\n");
  675.         if(ftp->verbose >= V_SHORT)
  676.             tprintf("Get complete: %lu bytes in %lu sec (%lu/sec)\n",
  677.              total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
  678.     } else {
  679.         tprintf("Error or abort during data transfer\n");
  680.     }
  681.     /* Get the "Sent" message */
  682.     getresp(ftp,200);
  683.  
  684.     ftp->state = COMMAND_STATE;
  685.     ftp->verbose = vsave;
  686.     return 0;
  687. }
  688.  
  689. /* Send a file. Syntax: put <local name> [<remote name>] */
  690. static int
  691. doput(argc,argv,p)
  692. int argc;
  693. char *argv[];
  694. void *p;
  695. {
  696.     char *remotename,*localname,*mode;
  697.     register struct ftpcli *ftp;
  698.     int i,resp,control;
  699.     unsigned long total;
  700.     FILE *fp;
  701.     struct sockaddr_in lsocket,lcsocket;
  702.     int32 startclk,rate;
  703.  
  704.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  705.         return -1;
  706.     control = ftp->control;
  707.  
  708.     if(ftp == NULLFTP){
  709.         tprintf(Notsess);
  710.         return 1;
  711.     }
  712.     localname = argv[1];
  713.     if(argc < 3)
  714.         remotename = localname;
  715.     else
  716.         remotename = argv[2];
  717.  
  718.     if(ftp->type == IMAGE_TYPE)
  719.         mode = READ_BINARY;
  720.     else
  721.         mode = READ_TEXT;
  722.  
  723.     /* Send TYPE message, if necessary */
  724.     if(ftp->typesent != ftp->type){
  725.         switch(ftp->type){
  726.         case ASCII_TYPE:
  727.             usprintf(control,"TYPE A\n");
  728.             break;
  729.         case IMAGE_TYPE:
  730.             usprintf(control,"TYPE I\n");
  731.             break;
  732.         case LOGICAL_TYPE:
  733.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  734.             break;
  735.         }
  736.         ftp->typesent = ftp->type;
  737.         resp = getresp(ftp,200);
  738.         if(resp == -1 || resp > 299){
  739.             return 1;
  740.         }
  741.     }
  742.     if((fp = fopen(localname,mode)) == NULLFILE){
  743.         tprintf("Can't read %s: %s\n",localname,sys_errlist[errno]);
  744.         return 1;
  745.     }
  746.     if(ftp->type == ASCII_TYPE && isbinary(fp)){
  747.         tprintf("Warning: File %s appears to be BINARY\n",localname);
  748.     }
  749.     /* Open the data connection */
  750.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  751.     listen(ftp->data,0);
  752.  
  753.     ftp->state = SENDING_STATE;
  754.  
  755.     /* Send the PORT message and wait for ack. Use the IP address
  756.      * on the local end of our control connection.
  757.      */
  758.     i = SOCKSIZE;
  759.     getsockname(ftp->data,(char *)&lsocket,&i);
  760.     i = SOCKSIZE;
  761.     getsockname(ftp->control,(char *)&lcsocket,&i);
  762.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  763.     sendport(control,&lsocket);
  764.     resp = getresp(ftp,200);
  765.     if(resp == -1 || resp > 299){
  766.         /* Error, quit */
  767.         fclose(fp);
  768.         close_s(ftp->data);
  769.         ftp->data = -1;
  770.         ftp->state = COMMAND_STATE;
  771.         return 1;
  772.     }
  773.     /* Generate the command to start the transfer and wait for ack */
  774.     usprintf(control,"STOR %s\n",remotename);
  775.     resp = getresp(ftp,100);
  776.     if(resp == -1 || resp >= 400){
  777.         /* Error, quit */
  778.         fclose(fp);
  779.         close_s(ftp->data);
  780.         ftp->data = -1;
  781.         ftp->state = COMMAND_STATE;
  782.         return 1;
  783.     }
  784.     /* Wait for the data connection to open. Otherwise the first
  785.      * block of data would go out with the SYN, and this may confuse
  786.      * some other TCPs
  787.      */
  788.     accept(ftp->data,NULLCHAR,(int *)NULL);
  789.  
  790.     startclk = Clock;
  791.  
  792.     total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  793.     close_s(ftp->data);
  794.     ftp->data = -1;
  795.     fclose(fp);
  796.  
  797.     startclk = Clock - startclk;
  798.     if(startclk != 0)
  799.         rate = total/startclk;
  800.     else
  801.         rate = 0;
  802.     if(total != -1) {
  803.         if(ftp->verbose >= V_HASH)
  804.             tprintf("\n");
  805.         if(ftp->verbose >= V_SHORT)
  806.         tprintf("Put complete: %lu bytes in %lu sec (%lu/sec)\n",
  807.          total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
  808.     } else {
  809.         tprintf("Error or abort during data transfer\n");
  810.     }
  811.     getresp(ftp,200);
  812.     ftp->state = COMMAND_STATE;
  813.     return 1;
  814. }
  815.  
  816. /* Abort a GET or PUT operation in progress. Note: this will leave
  817.  * the partial file on the local or remote system
  818.  */
  819. int
  820. doabort(argc,argv,p)
  821. int argc;
  822. char *argv[];
  823. void *p;
  824. {
  825.     register struct session *sp;
  826.     register struct ftpcli *ftp;
  827.  
  828.     sp = (struct session *)p;
  829.     if(sp == NULLSESSION)
  830.         return -1;
  831.  
  832.     /* Default is the current session, but it can be overridden with
  833.      * an argument.
  834.      */
  835.     if(argc > 1)
  836.         sp = sessptr(argv[1]);
  837.  
  838.     if(sp == NULLSESSION || sp->type != FTP){
  839.         tprintf("Not an active FTP session\n");
  840.         return 1;
  841.     }
  842.     ftp = sp->cb.ftp;
  843.  
  844.     switch(ftp->state){
  845.     case COMMAND_STATE:
  846.         tprintf("No active transfer\n");
  847.         return 1;
  848.     case SENDING_STATE:
  849.         /* Send a premature EOF.
  850.          * Unfortunately we can't just reset the connection
  851.          * since the remote side might end up waiting forever
  852.          * for us to send something.
  853.          */
  854.         shutdown(ftp->data,1);
  855.         break;
  856.     case RECEIVING_STATE:
  857.         /* Just blow away the receive socket */
  858.         shutdown(ftp->data,2);
  859.         break;
  860.     }
  861.     return 0;
  862. }
  863.  
  864. /* send PORT message */
  865. static void
  866. sendport(s,socket)
  867. int s;
  868. struct sockaddr_in *socket;
  869. {
  870.     /* Send PORT a,a,a,a,p,p message */
  871.     usprintf(s,"PORT %u,%u,%u,%u,%u,%u\n",
  872.         hibyte(hiword(socket->sin_addr.s_addr)),
  873.         lobyte(hiword(socket->sin_addr.s_addr)),
  874.         hibyte(loword(socket->sin_addr.s_addr)),
  875.         lobyte(loword(socket->sin_addr.s_addr)),
  876.         hibyte(socket->sin_port),
  877.         lobyte(socket->sin_port));
  878. }
  879.  
  880. /* Wait for, read and display response from FTP server. Return the result code.
  881.  */
  882. static int
  883. getresp(ftp,mincode)
  884. struct ftpcli *ftp;
  885. int mincode;    /* Keep reading until at least this code comes back */
  886. {
  887.     register char *line;
  888.     int rval;
  889.  
  890.     line = mallocw(LINELEN);
  891.     for(;;){
  892.         /* Get line */
  893.         if(recvline(ftp->control,line,LINELEN) == -1){
  894.             rval = -1;
  895.             break;
  896.         }
  897.         rip(line);        /* Remove cr/lf */
  898.         rval = atoi(line);
  899.         if(rval >= 400 || ftp->verbose >= V_NORMAL)
  900.             tprintf("%s\n",line);    /* Display to user */
  901.  
  902.         /* Messages with dashes are continued */
  903.         if(line[3] != '-' && (rval = atoi(line)) >= mincode)
  904.             break;
  905.     }
  906.     free(line);
  907.     return rval;
  908. }
  909.  
  910. /* Issue a prompt and read a line from the user */
  911. static int
  912. getline(sp,prompt,buf,n)
  913. struct session *sp;
  914. char *prompt;
  915. char *buf;
  916. int n;
  917. {
  918.     /* If there's something already there, don't issue prompt */
  919.     if(socklen(sp->input,0) == 0)
  920.         tprintf(prompt);
  921.  
  922.     usflush(sp->output);
  923.     return recvline(sp->input,buf,n);
  924. }
  925.  
  926. /* Attempt to log in the user whose name is in ftp->username and password
  927.  * in pass
  928.  */
  929. static char *
  930. ftpcli_login(ftp,host)
  931. struct ftpcli *ftp;
  932. char *host;
  933. {
  934.     char buf[80],*cp,*cp1;
  935.     FILE *fp;
  936.  
  937.     extern char *Hostfile;    /* List of user names and permissions */
  938.  
  939.     if((fp = fopen(Hostfile,"r")) == NULLFILE){
  940.         return NULLCHAR;
  941.     }
  942.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  943.         buf[strlen(buf)-1] = '\0';    /* Nuke the newline */
  944.         if(buf[0] == '#')
  945.             continue;    /* Comment */
  946.         if((cp = strchr(buf,' ')) == NULLCHAR)
  947.             /* Bogus entry */
  948.             continue;
  949.         *cp++ = '\0';        /* Now points to user name */
  950.         if(strcmp(host,buf) == 0)
  951.             break;        /* Found host name */
  952.     }
  953.     if(feof(fp)){
  954.         /* User name not found in file */
  955.         fclose(fp);
  956.         return NULLCHAR;
  957.     }
  958.     fclose(fp);
  959.     /* Look for space after user field in file */
  960.     if((cp1 = strchr(cp,' ')) == NULLCHAR)
  961.         /* if not there then we'll prompt */
  962.         ftp->password = NULLCHAR;
  963.     else
  964.         *cp1++ = '\0';        /* Now points to password */
  965.         if(strcmp(cp,"*") == 0)
  966.             cp1 = "anonymous";
  967.         ftp->password = strdup(cp1);
  968.     return strdup(cp);
  969. }
  970.  
  971.